#include "GBWidget.h"
#include "GBLabel.h"
#include "GBTrackBar.h"
#include "GBButton.h"
#include "..\..\src\Util\Collision.h"
#include "..\..\src\systems\Input System\Input.h"
#include <memory.h>
#include <iostream>

using Collision::RayToRectangle;

GBWidget::GBWidget() : m_bIsAutoSized(true), m_AlignType(ALIGN_LEFT), m_pParent(0)
{
	memset(m_pFunctions, 0, sizeof(FuncPtr) * WIDGET_EVENT_MAX);
}

void GBWidget::SetEventFunction(WidgetEvent _event, FuncPtr pPtr)
{
	if(_event != WIDGET_EVENT_MAX)
		this->m_pFunctions[_event] = pPtr;
}

void GBWidget::Copy(const GBWidget* pRef)
{
	if(pRef == NULL) return;

	// Shallow copy over common types
	this->m_pParent = pRef->m_pParent;
	this->m_AlignType = pRef->m_AlignType;
	this->m_bIsAutoSized = pRef->m_bIsAutoSized;
	this->m_pParent = pRef->m_pParent;
	this->m_fSpeed = pRef->m_fSpeed;
	this->m_color = pRef->m_color;
	this->m_isVisible = pRef->m_isVisible;
	this->m_local = pRef->m_local;
	this->m_meshID = pRef->m_meshID;
	this->m_orientation = pRef->m_orientation;
	this->m_shaderID = pRef->m_shaderID;
	this->m_velocity = pRef->m_velocity;

	// Copying over event func pts
	memcpy(this->m_pFunctions, pRef->m_pFunctions, sizeof(FuncPtr) * WIDGET_EVENT_MAX);
}

void GBWidget::Render()
{

}

void GBWidget::Update(const float fTimeStep)
{
	// Offset according to PARENT SPACE
	if(m_pParent != NULL)
	{
		switch(m_AlignType)
		{
		case ALIGN_LEFT:
			{
				//m_position.x = m_pParent->
			}
		case ALIGN_CENTER:
			{

			}
		case ALIGN_RIGHT:
			{

			}
		};
	}
}

bool MouseToWidget(const GBWidget* pWidget)
{
	if(!pWidget) return false;
	if(!pWidget->IsVisible()) return false;

	vec2 rayEnter, rayExit;	// Intersection points
	vec2 min, max;

	// We now have the "real" window space mouse position
	vec2 start = GetMousePosition();

	// Check special cases for collision
	switch(pWidget->Type())
	{
	case WIDGET_TYPE_LABEL:
		{
			// Label only testing this is the "special" case:
			GBLabel* pLabel = (GBLabel*)pWidget;
			const GBFont* pFont = pLabel->GetFont();

			// No font = no strings sorry
			if(pFont == NULL) 
				return false;

			const std::vector<GBLabel::LabelString>& strings = pLabel->GetString();
			unsigned strLength = 0;
			unsigned numStrings = (unsigned)strings.size();
			for (unsigned i = 0; i < numStrings; i++)
			{
				const std::string& string = strings[i].string;
				const matrix4& textMat = strings[i].local;

				min = Vec2(textMat.wAxis().X(), textMat.wAxis().Y());

				// Get the mesh info for a single char in the string
				strLength = (unsigned)strings[i].string.length();
				for (unsigned j = 0; j < strLength; j++)
				{
					// X: width Y: height
					const vec3& dims = pFont->GetGlyphMesh(strings[i].string[j])->vertices[2];

					max = vec2(min.X() + dims.X(), min.Y() + dims.Y());

					if(RayToRectangle(start, vec2(1, 1), 
						min + pFont->GetGlyphMesh(strings[i].string[j])->texturePad, 
						max, rayEnter, rayExit))
					{
						return true;
					}

					// increase the min now for every letter (procedural kerning)
					min.X() += pFont->GetGlyphWidth(strings[i].string[j]);
				}
			}
		}
		break;
	case WIDGET_TYPE_TRACK_BAR:
		{
			GBTrackBar* pTrackBar = (GBTrackBar*)pWidget;

			const vec2 position = pTrackBar->GetTrackerPosition();
			const vec2 size = pTrackBar->GetTrackerSize();
			const vec2 min = position - size;
			const vec2 max = position + size;

			// TODO: Move the mouse also in this check
			if(RayToRectangle(start, vec2(1, 1), min, max, rayEnter, rayExit))
				return true;
		}
		break;
	case WIDGET_TYPE_BUTTON:
		{
			GBButton* pButton = (GBButton*)pWidget;

			const vec2 position = vec2(pButton->GetLocalMatrix().wAxis().X(), pButton->GetLocalMatrix().wAxis().Y());
			const vec2 size = pButton->GetSize();

			// Do some simple checks to see which way the label is algined, so we can do precise collision correctly
			vec2 min, max;

			switch(pButton->GetAlignment())
			{
			case ALIGN_RIGHT:
				{
					min.X() = position.X() - size.X();
					min.Y() = position.Y();
					max.X() = position.X();
					max.Y() = position.Y() + size.Y();
				}
				break;
			case ALIGN_LEFT:
				{
					min = position;
					max = position + size;
				}
				break;
			case ALIGN_CENTER:
				{
					min.X() = position.X() + size.X() * 0.5f;
					min.Y() = position.Y() + size.Y();
					max.X() = position.X() - size.X() * 0.5f;
					max.Y() = position.Y();
				}
				break;
			};

			if(RayToRectangle(start, vec2(1, 1), min, max, rayEnter, rayExit))
				return true;
		}
		break;
	}

	return false;
}

